package com.abc.scholarship.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import com.abc.scholarship.common.constants.CacheConstant;
import com.abc.scholarship.common.enums.StatusEnum;
import com.abc.scholarship.common.utils.ExcelUtil;
import com.abc.scholarship.common.utils.MajorGradeNumUtil;
import com.abc.scholarship.common.utils.UserHolder;
import com.abc.scholarship.domain.dto.ResultDto;
import com.abc.scholarship.domain.entity.*;
import com.abc.scholarship.domain.page.PageDTO;
import com.abc.scholarship.domain.page.PageQuery;
import com.abc.scholarship.domain.vo.resp.ApplyVO;
import com.abc.scholarship.domain.vo.resp.ResultResp;
import com.abc.scholarship.exception.BusinessException;
import com.abc.scholarship.mapper.ApplyResMapper;
import com.abc.scholarship.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.abc.scholarship.common.constants.CacheConstant.APPLY_RES_EXPIRE_TIME;

/**
 * <p>
 * 结果表 服务实现类
 * </p>
 *
 * @author jzw
 * @since 2024-07-02
 */
@Service
public class ApplyResServiceImpl extends ServiceImpl<ApplyResMapper, ApplyRes> implements ApplyResService {

    @Autowired
    private ApplyResMapper applyResMapper;

    @Autowired
    private ScholarshipService scholarshipService;

    @Autowired
    private MajorGradeNumService majorGradeNumService;

    @Autowired
    private UserService userService;

    @Autowired
    private StudentService studentService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private HttpServletResponse response;

    @Override
    @Transactional
    public void postScholarshipRes() {
        Integer year = LocalDate.now().getYear();
        // 先删除今年已经保存的数据
        applyResMapper.delete(new LambdaUpdateWrapper<ApplyRes>()
                .eq(ApplyRes::getYear, year));
        String key = CacheConstant.getKey(CacheConstant.APPLY_RES, year);
        stringRedisTemplate.delete(key);
        // 录用的人数
        Map<Long, List<ResultDto>> resMap = applyResMapper.getScholarshipSort(year)
                .stream()
                .collect(Collectors.groupingBy(ResultDto::getMajorGradeId));
        // 所有奖学金
        List<Scholarship> scholarships = scholarshipService.list(
                new LambdaQueryWrapper<Scholarship>().eq(Scholarship::getStatus, StatusEnum.FROZEN.getValue())
                )
                .stream()
                .sorted(new Comparator<Scholarship>() {
                    @Override
                    public int compare(Scholarship o1, Scholarship o2) {
                        return o1.getRatio() - o2.getRatio();
                    }
                })
                .collect(Collectors.toList());
        // 所有专业的人数
        Map<String, MajorGradeNum> majorGradeNumMap = majorGradeNumService.list()
                .stream()
                .collect(Collectors.toMap(MajorGradeNum::getMajorGrade, Function.identity()));
        // 奖学金人数
        List<ApplyRes> resList = new ArrayList<>();
        for (Map.Entry<Long, List<ResultDto>> item : resMap.entrySet()) {
            resList.addAll(getResSortList(item.getValue(), scholarships, majorGradeNumMap, year));
        }
        // 保存结果
        saveBatch(resList);

    }

    private List<ApplyRes> getResSortList(List<ResultDto> itemValues, List<Scholarship> scholarships,
                                           Map<String, MajorGradeNum> majorGradeNumMap, Integer year) {
        List<ApplyRes> res = new ArrayList<>();
        // 为空
        if (CollUtil.isEmpty(itemValues)){
            return res;
        }
        String key = MajorGradeNumUtil.getMajorGrade(itemValues.get(0).getClassName());
        Integer total = majorGradeNumMap.get(key).getNum();
        // 获取该专业每个奖学金应该获取的人数
        List<Integer> nums = getScholarPartNum(total, scholarships);
        // 选出奖学金
        for(int i = 0, j = 0, idx = 0; i < itemValues.size(); i++, j++){
            if(j == nums.get(idx)) {
                j = 0; idx ++;
            }
            if(idx >= nums.size()) {
                break;
            }
            ResultDto resultDto = itemValues.get(i);
            res.add(new ApplyRes(resultDto.getUId(), scholarships.get(idx).getId(), year));
        }
        return res;
    }

    private List<Integer> getScholarPartNum(Integer total, List<Scholarship> scholarships) {
        return scholarships.stream()
                .map(item -> getPeopleNum(total, item.getRatio()))
                .sorted()
                .collect(Collectors.toList());
    }

    public Integer getPeopleNum(Integer total, Integer ratio){
        return total * ratio / 100;
    }

    @Override
    public List<ResultResp> getScholarshipRes() {
        Integer year = LocalDate.now().getYear();
        // 先查询缓存
        String key = CacheConstant.getKey(CacheConstant.APPLY_RES, year);
        String resStr = stringRedisTemplate.opsForValue().get(key);
        if (Objects.nonNull(resStr)){
            return JSONUtil.toList(resStr, ResultResp.class);
        }
        // 无缓存
        List<ApplyRes> applyResList = list(new LambdaUpdateWrapper<ApplyRes>()
                .eq(ApplyRes::getYear, year));
        if (CollUtil.isEmpty(applyResList)){
            // 无结果直接返回
            return new ArrayList<>();
        }
        List<Long> uIds = applyResList.stream()
                .map(ApplyRes::getUId).collect(Collectors.toList());
        Map<Long, User> userMap = userService.list(new LambdaUpdateWrapper<User>().in(User::getId, uIds))
                .stream()
                .collect(Collectors.toMap(User::getId, Function.identity()));
        Map<Long, Student> studentMap = studentService.list(new LambdaUpdateWrapper<Student>().in(Student::getUId, uIds))
                .stream()
                .collect(Collectors.toMap(Student::getUId, Function.identity()));
        Map<Long, Scholarship> scholarshipMap = scholarshipService.list().stream()
                .collect(Collectors.toMap(Scholarship::getId, Function.identity()));

        List<ResultResp> resp = applyResList.stream().map(item -> ResultResp.build(item,
                userMap.get(item.getUId()),
                studentMap.get(item.getUId()),
                scholarshipMap.get(item.getScholarshipId()))
        ).collect(Collectors.toList());
        // 加入缓存
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(resp), APPLY_RES_EXPIRE_TIME, TimeUnit.HOURS);

        return resp;
    }

    @Override
    public void exportResult() {
        // 查询结果是否存在
        Integer year = LocalDate.now().getYear();
        String key = CacheConstant.getKey(CacheConstant.APPLY_RES, year);
        String resStr = stringRedisTemplate.opsForValue().get(key);
        // 导出结果
        List<ResultResp> scholarshipRes;
        if (Objects.isNull(resStr)){
           scholarshipRes = getScholarshipRes();
        } else {
           scholarshipRes = JSONUtil.toList(resStr, ResultResp.class);
        }
        try {
            ExcelUtil.outputExcel(response, scholarshipRes, ResultResp.class, "奖学金结果");
        } catch (Exception e){
            throw new BusinessException(e.getMessage());
        }
    }

    @Override
    public List<ResultResp> stuGetScholarshipRes() {
        Long uId = UserHolder.getUId();
        return applyResMapper.getListByUid(uId);
    }
}
